﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.ServiceProcess;
using System.Text;
using System.Threading;


namespace GPSHHEM_81ListenerServerService
{
    // State object for reading client data asynchronously
    public class StateObject
    {
        // Client socket.
        public Socket workSocket = null;
        // Size of receive buffer.
        public const int BufferSize = 512;
        // Receive buffer.
        public byte[] buffer = new byte[BufferSize];
    }


    public partial class GPSHHEM81ProtocolListenerServerService : ServiceBase
    {
        public const int BufferSize = 512;

        // Enum Field
        enum MessageField
        { 
            DeviceID = 0,
            DeviceDatetime = 1,
            DeviceLatitude = 2,
            DeviceLongitude = 3
        }


        private static bool IsThreadAlive = true;
        public static Thread TCPListenerThread = new Thread(StartListening);

        // Thread signal.
        public static ManualResetEvent allDone = new ManualResetEvent(false);

        public GPSHHEM81ProtocolListenerServerService()
        {
            InitializeComponent();
        }

        protected override void OnStart(string[] args)
        {
            try
            {
                GPSTrackingDBCustom.Log.WindowsServiceLog.Info("--- GPS Tracking Server Start Listening HandHeld EM-81 V1 ---", "HandHeld EM-81");
                GPSTrackingDBCustom.Log.WindowsServiceLog.Info("IP: " + System.Net.Dns.GetHostName(), "HandHeld EM-81");
                GPSTrackingDBCustom.Log.WindowsServiceLog.Info("Port: " + Properties.Settings.Default.Port.ToString(), "HandHeld EM-81");
                GPSTrackingDBCustom.Log.WindowsServiceLog.Info("Max Connections: " + Properties.Settings.Default.MaxConnections.ToString(), "HandHeld EM-81");

                IsThreadAlive = true;
                TCPListenerThread.Priority = ThreadPriority.Highest;
                TCPListenerThread.IsBackground = true;
                TCPListenerThread.Start();
                
            }
            catch (Exception ex)
            {


                GPSTrackingDBCustom.Log.WindowsServiceLog.Error("Error - OnStart Message:" + ex.Message, "HandHeld EM-81");
                GPSTrackingDBCustom.Log.WindowsServiceLog.Error("Hand Held EM-81 Server is DOWN", "HandHeld EM-81");
                IsThreadAlive = false;
            }

        }

        protected override void OnStop()
        {
            IsThreadAlive = false;
            GPSTrackingDBCustom.Log.WindowsServiceLog.Warning("--- OnStop GPS Tracking Server HandHeld EM-81 Is DOWN ---", "HandHeld EM-81");
        }

        public static void StartListening()
        {
            IPEndPoint ep = new IPEndPoint(IPAddress.Any, Properties.Settings.Default.Port);

            // Create a TCP/IP socket for listner.
            Socket listenerSock = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);            

            // Bind the socket to the endpoint and wait for listen for incoming connections.
            try
            {
                listenerSock.Bind(ep);
                listenerSock.Listen(Properties.Settings.Default.MaxConnections);

                while (IsThreadAlive)
                {
                    try
                    {
                        // Set the event to nonsignaled state.
                        allDone.Reset();

                        // Start an asynchronous socket to listen for connections.
                        GPSTrackingDBCustom.Log.WindowsServiceLog.Info("Waiting for a connection ...", "HandHeld EM-81");
                        listenerSock.BeginAccept(new AsyncCallback(AcceptCallback), listenerSock);

                        // Wait until a connection is made before continuing.
                        allDone.WaitOne();
                    }
                    catch (Exception ex)
                    {
                        GPSTrackingDBCustom.Log.WindowsServiceLog.Error("Error - StartListening Waiting for a connection ... Message:" + ex.Message, "HandHeld EM-81");
                        listenerSock.Shutdown(SocketShutdown.Both);
                        listenerSock.Close();
                    }

                }

            }
            catch (Exception ex)
            {
                IsThreadAlive = false;               
                GPSTrackingDBCustom.Log.WindowsServiceLog.Error("Error..... Message:" + ex.Message , "HandHeld EM-81");
                GPSTrackingDBCustom.Log.WindowsServiceLog.Error("Hand Held EM-81 Server is DOWN", string.Empty);
            }
        }

        public static void AcceptCallback(IAsyncResult ar)
        {
            try
            {
                // Signal the main thread to continue.
                allDone.Set();

                // Get the socket that handles the client request.
                Socket listener = (Socket)ar.AsyncState;
                Socket handler = listener.EndAccept(ar);

                // Create the state object.
                StateObject MessageDevice = new StateObject();
                MessageDevice.workSocket = handler;
                handler.BeginReceive(MessageDevice.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReadCallback), MessageDevice);
            }
            catch (Exception ex)
            {
               // IsThreadAlive = false;
                GPSTrackingDBCustom.Log.WindowsServiceLog.Error("Error - AcceptCallback Message:" + ex.Message, "HandHeld EM-81");
               //GPSTrackingDBCustom.Log.WindowsServiceLog.Error("Hand Held EM-81 Server is DOWN", "HandHeldEM-81");                
            }
        }

        public static void ReadCallback(IAsyncResult ar)
        {
              // Retrieve the state object and the handler socket
                // from the asynchronous state object.
                StateObject MessageDevice = (StateObject)ar.AsyncState;
                Socket handler = MessageDevice.workSocket;

                // Read data from the client socket.
                int bytesRead = handler.EndReceive(ar);

                try
                {
                    //No Message 
                    if (bytesRead <= 0)
                    {
                        GPSTrackingDBCustom.Log.WindowsServiceLog.Info("Disconnected... gps device. No Message", "HandHeld EM-81");
                        handler.Shutdown(SocketShutdown.Both);
                        handler.Close();

                        return;
                    }

                    //Process Message Hande Held
                    var New_MessageDevice = MessageDevice.buffer.TakeWhile((v, index) => MessageDevice.buffer.Skip(index).Any(w => w != 0x00)).ToArray();
                    string Message = System.Text.UTF8Encoding.UTF8.GetString(New_MessageDevice);

                    if (bytesRead < 10)
                    {
                        GPSTrackingDBCustom.Log.WindowsServiceLog.Info("Message Device: " + Message + "Message Invalid Read Again", "HandHeld EM-81");
                        // Not all data received. Get more.
                        handler.BeginReceive(MessageDevice.buffer, 0, StateObject.BufferSize, 0, new AsyncCallback(ReadCallback), MessageDevice);
                        return;
                    }
                    else
                    {
                        GPSTrackingDBCustom.Log.WindowsServiceLog.Info("Message Device: " + Message, "HandHeld EM-81");
                       
                            string[] MessageArray = Message.Split(new char[] { '|' });

                            string DeviceID = MessageArray[(int)MessageField.DeviceID];
                            string DeviceDateTime = MessageArray[(int)MessageField.DeviceDatetime];
                            string DeviceLatitude = MessageArray[(int)MessageField.DeviceLatitude];
                            string DeviceLongitude = MessageArray[(int)MessageField.DeviceLongitude];


                            //Fix problem Device name by Steven
                            int indexAgentName = DeviceID.IndexOf('A');
                            DeviceID = DeviceID.Substring(indexAgentName);

                            //Convert NMEA
                            //Convert Lat.
                            int LatDegree = Convert.ToInt32(DeviceLatitude.Substring(0, 2));
                            double LatMinute = Convert.ToDouble(DeviceLatitude.Substring(2));

                            LatMinute = LatDegree + (LatMinute / 60);

                            //Convert Long.
                            int LongDegree = Convert.ToInt32(DeviceLongitude.Substring(0, 3));
                            double LongMinute = Convert.ToDouble(DeviceLongitude.Substring(3));

                            LongMinute = LongDegree + (LongMinute / 60);
                            LongMinute = LongMinute * -1;

                            //Insert to Database
                            GPSTrackingDBV2.Entities.TrackData TrackDataEntity = new GPSTrackingDBV2.Entities.TrackData();
                            try
                            {
                                GPSTrackingDBCustom.Log.WindowsServiceLog.Info("Inserting track message to database ...", "HandHeld EM-81");
                                GPSTrackingDBV2.Entities.TrackDataLastLocation TrackDataLastLocationEntity = new GPSTrackingDBV2.Entities.TrackDataLastLocation();

                                TrackDataLastLocationEntity.GpsImei = TrackDataEntity.GpsImei = DeviceID;
                                TrackDataLastLocationEntity.DateTime = TrackDataEntity.DateTime = DateTime.Now;
                                TrackDataLastLocationEntity.Latitude = TrackDataEntity.Latitude = LatMinute;
                                TrackDataLastLocationEntity.Longitude = TrackDataEntity.Longitude = LongMinute;
                                TrackDataLastLocationEntity.Heading = 0;
                                TrackDataLastLocationEntity.Speed = 0;

                                GPSTrackingDBV2.Data.DataRepository.TrackDataProvider.Insert(TrackDataEntity);

                                //Update last location  
                                GPSTrackingDBCustom.Log.WindowsServiceLog.Info("Updating last track message location to database ...", "HandHeld EM-81");
                                try
                                {
                                    GPSTrackingDBV2Custom.Data.DataRepository.TrackDataLastLocationProvider.UpdateOrInsert(TrackDataLastLocationEntity);
                                }
                                catch (Exception ex)
                                {
                                    GPSTrackingDBCustom.Log.WindowsServiceLog.Error("Error - Updating last track message:" + ex.Message, "HandHeld EM-81");
                                    // Disconnect 
                                    handler.Shutdown(SocketShutdown.Both);
                                    handler.Close();
                                }


                            }
                            catch (Exception ex)
                            {
                                GPSTrackingDBCustom.Log.WindowsServiceLog.Error("Error - Inserting track message:" + ex.Message, "HandHeld EM-81");
                                // Disconnect 
                                handler.Shutdown(SocketShutdown.Both);
                                handler.Close();

                            }

                            // Disconnect Hand Held                        
                            Send(handler, Encoding.ASCII.GetBytes("OK"));
                        
                    }
                }
                catch (Exception ex)
                {
                    GPSTrackingDBCustom.Log.WindowsServiceLog.Error("Error - Message invalid:" + ex.Message, "HandHeld EM-81");
                    // Disconnect 
                    handler.Shutdown(SocketShutdown.Both);
                    handler.Close();     
                }

        }

        private static void Send(Socket handler, byte[] MessageSend)
        {
            try
            {
                // Begin sending the data to the remote device.               
                handler.BeginSend(MessageSend, 0, MessageSend.Length, 0, new AsyncCallback(SendCallback), handler);
            }
            catch (Exception ex)
            {
                GPSTrackingDBCustom.Log.WindowsServiceLog.Error("Error - Send Message:" + ex.Message, "HandHeld EM-81");
                // Disconnect 
                handler.Shutdown(SocketShutdown.Both);
                handler.Close();     
            }
        }

        private static void SendCallback(IAsyncResult ar)
        {
            // Retrieve the socket from the state object.
            Socket handler = (Socket)ar.AsyncState;
            try
            {                
                // Complete sending the data to the remote device.
                //int bytesSent = handler.EndSend(ar);
                GPSTrackingDBCustom.Log.WindowsServiceLog.Info("Disconnected... gps device.", "HandHeld EM-81");
                handler.Shutdown(SocketShutdown.Both);
                handler.Close();

            }
            catch (Exception ex)
            {
                GPSTrackingDBCustom.Log.WindowsServiceLog.Error("Error - SendCallBack..... " + ex.Message, "HandHeld EM-81");
                handler.Shutdown(SocketShutdown.Both);
                handler.Close();
            }
        }        
    }
}
